
#include <cstdio>
#include <BDate>
#include <BTime>
#include <BFolder>
#include "member_BFolder.h"

using namespace BWE;

#define member					(*(member_BFolder*)_ptr)
#define member_allocate()		_ptr = new member_BFolder
#define member_release()		delete (member_BFolder*)_ptr

#ifdef _LINUX_
BString BFolder::Current()
{
	char buffer[256];
	char* ret = getcwd(buffer, 256);
	return buffer;
}
bool BFolder::ChangeCurrent(const BString& path)
{
	int ret = chdir(path.cstr());
	return ret == 0;
}
BString BFolder::FullPath(const BString& path)
{
	return path;
}
bool BFolder::Create(const BString& folderName)
{
	int ret = mkdir(folderName.cstr(), 1);
	return ret == 0;
}
bool BFolder::Remove(const BString& folderName)
{
	int ret = rmdir(folderName.cstr());
	return ret == 0;
}
bool BFolder::Rename(const BString& oldName, const BString& newName)
{
	int ret = rename(oldName.cstr(), newName.cstr());
	return ret == 0;
}
bool BFolder::Exists(const BString& path)
{

	return true;
}
bool BFolder::open()
{
	if(member.isOpened)
		return true;

	member.entries.clear();
	member.dir = opendir(member.path.cstr());
	if(!member.dir)
		return false;

	while(dirent* de = readdir(member.dir))
	{
		BString name = de->d_name;
		if(name == '.' || name == "..")
			continue;

		Attrib type = Attrib_None;
		switch(de->d_type)
		{
		case DT_BLK:
			type = Attrib_File;
			break;
		case DT_CHR:
			type = Attrib_File;
			break;
		case DT_DIR:
			type = Attrib_Folder;
			break;
		case DT_FIFO:
			type = Attrib_Pipe;
			break;
		case DT_LNK:
			type = Attrib_Link;
			break;
		case DT_REG:
			type = Attrib_Regular;
			break;
		case DT_SOCK:
			type = Attrib_Socket;
			break;
		default:
			type = Attrib_None;
			break;
		}
		BEntry* entry = new BEntry();
		entry->setName(name);
		entry->setType(type);
		entry->setPath(this->path());
		member.entries.append(entry);
	}
	member.isOpened = true;
	return member.isOpened;
}

bool BFolder::close()
{
	if(member.isOpened)
	{
		int ret = closedir(member.dir);
		member.isOpened = false;
		member.entries.clear();
		return ret == 0;
	}
	return false;
}

#endif

#ifdef _WINDOWS_SYSTEM_

BString BFolder::Current()
{
	char buffer[256];
	DWORD ret = GetCurrentDirectoryA(256, buffer);
	return buffer;
}
bool BFolder::ChangeCurrent(const BString& path)
{
	BOOL ret = ::SetCurrentDirectoryA(path.cstr());
	return (bool)ret;
}
BString BFolder::FullPath(const BString& path)
{
	char buffer[256];
	DWORD ret = GetFullPathNameA(path.cstr(), 256, buffer, 0);
	return buffer;
}

bool BFolder::Create(const BString& folderName)
{
	return CreateDirectoryA(folderName.cstr(), 0);
}
bool BFolder::Remove(const BString& folderName)
{
	return RemoveDirectoryA(folderName.cstr());
}
bool BFolder::Rename(const BString& oldName, const BString& newName)
{
	return false;
}
bool BFolder::Exists(const BString& path)
{
	WIN32_FIND_DATA EntryData;
	HANDLE hSearch = FindFirstFile(path.cstr(), &EntryData);
	if (hSearch == INVALID_HANDLE_VALUE)
		return false;
	FindClose(hSearch);
	return true;
}

bool BFolder::exists() const
{
	WIN32_FIND_DATA EntryData;
	HANDLE hSearch = FindFirstFile(member.path.cstr(), &EntryData);
	if (hSearch == INVALID_HANDLE_VALUE)
		return false;
	FindClose(hSearch);
	return true;
}

bool BFolder::open()
{
	if(member.isOpened)
		return true;

	member.entries.clear();

	BString fileStr;
	if (member.path.empty())
	{
		fileStr = '*';
	}
	else
	{
		fileStr  = member.path + '/' + '*';
	}

	WIN32_FIND_DATA EntryData;
	HANDLE hSearch = FindFirstFile(fileStr.cstr(), &EntryData);
	if(hSearch == INVALID_HANDLE_VALUE)
		return false;

	while(FindNextFile(hSearch, &EntryData))
	{
		BString name = EntryData.cFileName;
		if(name == '.' || name == "..")
			continue;

		EntryFlag attrib = Attrib_File;

		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			attrib |= Attrib_Folder;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_DEVICE)
			attrib |= Attrib_Drive;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_VIRTUAL)
			attrib |= Attrib_File;
		if (EntryData.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA)
			attrib |= Attrib_File;

		if (attrib == Attrib_None)
			continue;

		if (member.entryFlag & attrib)
		{
			BEntry* entry = new BEntry();
			entry->setAttrib((Attrib)attrib);
			entry->setName(name);
			entry->setPath(this->path() + '/' + name);

			SYSTEMTIME st;
			FileTimeToSystemTime(&EntryData.ftCreationTime, &st);
			BDate date(st.wYear, st.wMonth, st.wDay);
			BTime time(st.wHour, st.wMinute, st.wSecond);
			entry->setDate(date);
			entry->setTime(time);
			entry->setSize(EntryData.nFileSizeLow);

			member.entries.append(entry);
		}
	}
	FindClose(hSearch);

	member.isOpened = true;

	return true;
}
bool BFolder::close()
{
	if(member.isOpened)
	{
		member.isOpened = false;
		member.entries.clear();
		return true;
	}
	return false;
}

#endif

BFolder::BFolder()
{
	member_allocate();
}
BFolder::BFolder(const BString& folderPath, EntryFlag entryFlag)
{
	member_allocate();
	setPath(folderPath);
	setEntryFlag(entryFlag);
}
BFolder::~BFolder()
{
	member_release();
}

bool BFolder::isOpened() const
{
	return member.isOpened;
}

void BFolder::setPath(const BString& path)
{
	if (member.path != path)
	{
		member.path = path;
		if (member.isOpened)
		{
			close();
		}
	}
}
const BString& BFolder::path() const
{
	return member.path;
}

void BFolder::setEntryFlag(EntryFlag entryFlag)
{
	if (member.entryFlag != entryFlag)
	{
		member.entryFlag = entryFlag;
	}
}
EntryFlag BFolder::entryFlag() const
{
	return member.entryFlag;
}

void BFolder::setNameFilter(const BString& nameFilter)
{
	if (member.nameFilter != nameFilter)
	{
		member.nameFilter = nameFilter;
	}
}
const BString& BFolder::nameFilter() const
{
	return member.nameFilter;
}

bool BFolder::contain(const BString& entryName)
{
	for(int i = 0; i < member.entries.size(); i++)
		if(member.entries[i]->name() == entryName)
			return true;
	return false;
}

int BFolder::entryCount() const
{
	return member.entries.size();
}
BEntry* BFolder::entry(int index) const
{
	return member.entries(index).ptr();
}
BEntry* BFolder::entry(const BString& name) const
{
	for (int i = 0; i < member.entries.size(); i++)
	{
		BEntry* entry = member.entries[i];
		if (entry->name() == name)
			return entry;
	}
	return 0;
}
